function Topic(){
  this.name = null;
  this.uri = null;

  this.viewpoint = {};
  this.relatedTopics = new Array();
  this.entities = new Array();
};

Topic.prototype.toXml = function() {
  var xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
  xmlStr += "<!DOCTYPE entity SYSTEM \"" + TOPIC_DTD + "\">\n";
  xmlStr += "<topic ";
  if(this.uri != null && this.uri.trim() != "")
    xmlStr += "href=\"" + this.uri + "\" ";
  if(this.name != null && this.name.trim() != "")
    xmlStr += "name=\"" + this.name + "\" ";
  xmlStr += ">";

  // viewpoint
  xmlStr += "<viewpoint ";
  if (typeof this.viewpoint.href != 'undefined' && this.viewpoint.href != "")
    xmlStr += "href=\"" + this.viewpoint.href + "\"";
  xmlStr += ">";
  if (typeof this.viewpoint.name != 'undefined' && this.viewpoint.name != "")
    xmlStr += this.viewpoint.name;
  xmlStr += "</viewpoint>";

  // related topics
  for(i = 0; i < this.relatedTopics.length; i++){
    var href, name, relationType;
    if ('undefined' == typeof this.relatedTopics[i].href)
      href = "";
    else
      href = this.relatedTopics[i].href + "";
    if ('undefined' == typeof this.relatedTopics[i].name)
      name = "";
    else
      name = this.relatedTopics[i].name + "";
    if ('undefined' == typeof this.relatedTopics[i].relationType)
      relationType = "read";
    else
      relationType = this.relatedTopics[i].relationType + "";
    xmlStr += "<relatedTopic href=\"" + href.toXml() + "\" relationType=\"" + relationType.toXml() + "\">" + name.toXml() + "</topic>";
  }

  for(i = 0; i < this.entities.length; i++){
    var href, name;
    if ('undefined' == typeof this.entities[i].href)
      href = "";
    else
      href = this.entities[i].href + "";
    if ('undefined' == typeof this.entities[i].name)
      name = "";
    else
      name = this.entities[i].name + "";
    xmlStr += "<entity href=\"" + href.toXml() + "\">" + name.toXml() + "</entity>";
  }
  xmlStr += "</topic>";
  return xmlStr;
}

Topic.prototype.parseXml = function(xmlStr){
  var t = new Topic();
  var xmlReader = Components.classes["@mozilla.org/saxparser/xmlreader;1"]
                          .createInstance(Components.interfaces.nsISAXXMLReader);
  xmlReader.contentHandler = {
    currentElement: null,
    entity: null,
    relatedTopic: null,

    startDocument: function() {},
		endDocument: function() {},

    startElement: function(uri, localName, qName, /*nsISAXAttributes*/ attributes) {
      this.currentElement = qName;
      //debug("[startElement] currentElement:" + this.currentElement ,1);

      if(qName == "topic")
        for(var i=0; i<attributes.length; i++) {
          if(attributes.getQName(i) == "name")
            t.name = attributes.getValue(i);
          if(attributes.getQName(i) == "href")
            t.uri = attributes.getValue(i);
        }

      if(qName == "viewpoint")
        for(var i=0; i<attributes.length; i++) {
          if(attributes.getQName(i) == "href")
            t.viewpoint.href = attributes.getValue(i);
        }

      if(qName == "relatedTopic"){
        this.relatedTopic = {};
        this.relatedTopic.name = "";
        for(var i=0; i<attributes.length; i++) {
          if(attributes.getQName(i) == "href")
            this.relatedTopic.href = attributes.getValue(i);
          if(attributes.getQName(i) == "relationType")
            this.relatedTopic.relationType = attributes.getValue(i);
        }
      }

      if(qName == "entity"){
        this.entity = {};
        this.entity.name = "";
        for(var i=0; i<attributes.length; i++)
          if(attributes.getQName(i) == "href")
            this.entity.href = attributes.getValue(i);
      }
    },

    endElement: function(uri, localName, qName) {
      //debug("[endElement] currentElement:" + this.currentElement + ", typeof:" + (typeof this.tmpTopic),1);
      if(this.currentElement == "relatedTopic" && this.relatedTopic != null){
        t.relatedTopics.push(this.relatedTopic);
        this.relatedTopic = null;
        //debug("[endElement] vp.topics.push:" + vp.topics.length ,1);
      }
      if(this.currentElement == "entity" && this.entity != null){
        t.entities.push(this.entity);
        this.entity = null;
        //debug("[endElement] vp.topics.push:" + vp.topics.length ,1);
      }
      this.currentElement = null;
    },

    characters: function(value) {
      //debug("[characters] currentElement:" + this.currentElement + ", typeof:" + (typeof this.currentElement) + ", value:" + value,1);
      if((typeof this.currentElement) != "string")
        return;
      if(this.currentElement == "relatedTopic"){
        this.relatedTopic.name = value;
      }
      if(this.currentElement == "entity"){
        this.entity.name = value;
      }
      if(this.currentElement == "viewpoint"){
        t.viewpoint.name = value;
      }
    },
    processingInstruction: function(target, data) {},
    ignorableWhitespace: function(whitespace) {},
    startPrefixMapping: function(prefix, uri) {},
    endPrefixMapping: function(prefix) {},

    // nsISupports
    QueryInterface: function(iid) {
      if(!iid.equals(Components.interfaces.nsISupports) &&
         !iid.equals(Components.interfaces.nsISAXContentHandler))
        throw Components.results.NS_ERROR_NO_INTERFACE;
      return this;
    }
  }
  xmlReader.parseFromString(xmlStr, "text/xml");
  return t;
}

Topic.prototype.dump = function() {
  debug("Topic name:" + this.name,1);
  debug("Topic uri:" + this.uri,1);
  debug("Viewpoint href:" + this.viewpoint.href + ", name:" + this.viewpoint.name, 1);
  for(var i =0; i <this.relatedTopics.length; i++){
	  var topic = this.relatedTopics[i];
	  debug("[topic] href:" + topic.href + ", name:" + topic.name + ", relationType:" + topic.relationType);
	}
  for(var i =0; i < this.entities.length; i++){
	  var entity = this.entities[i];
	  debug("[entity] href:" + entity.href + ", name:" + entity.name);
	}
}

Topic.prototype.httpGet = function() {
	if(this.uri == null){
		return null;
	}
	var getRequest = new GetRequest(this.uri);
	var http = new HTTPCore();
	var res = http.doOperation(getRequest);
	if(!res) return null;
	debug("[topic.js] Topic.httpGet URL:" + this.uri, 4);
	debug("[topic.js] Topic.httpGet Response:" + http.response, 4);
	var res = this.parseXml(http.response);
	if(res != null)
	  res.uri = this.uri;
	return res;
}

Topic.prototype.httpPost = function(uri, user, pass) {
	if(uri == null){
		return false;
	}
	var content = this.toXml();
  var req = new customRequest('POST', uri, content);
  var http = new HTTPCore();
	if(typeof user == "string" && user != "")
	  http.auth(user, pass);
	var res = http.doOperation(req);
	debug("[topic.js::httpPost] request uri:" + uri, 4);
	debug("[topic.js::httpPost] request body:" + content, 4);
	if(!res) return null;
	if(http.status == "205") return true;
	var topicUri = http.xmlHttp.getResponseHeader("Location");

	debug("[topic.js::httpPost] get res header:" + http.status, 4);
	debug("[topic.js::httpPost] get res body:" + http.response, 4);
	debug("[topic.js::httpPost] new topic Uri:" + topicUri, 4);
	return topicUri;
}


function topicTest(){

  debug("---------------------- test parseXml() -----------------------", 1);
  var xmlStr = '<?xml version="1.0" encoding="UTF-8"?>'
      +'<!DOCTYPE topic SYSTEM "http://test.hypertopic.org/argos/validator/dtd/topic.dtd">'
      +'<topic href="http://test.hypertopic.org/argos/viewpoint/1/topic/116/" name="uyyuyiu"><viewpoint href="http://test.hypertopic.org/argos/viewpoint/1/">Athos</viewpoint></topic>';

  var topic = new Topic();
  topic = topic.parseXml(xmlStr);
  topic.dump();

  debug("---------------------- test toXml() -----------------------", 1);
  xmlRes = topic.toXml();
  debug(xmlRes, 1);

  debug("---------------------- test httpGet() -----------------------", 1);
  topic = new Topic();
  topic.uri = "http://test.hypertopic.org/argos/viewpoint/1/topic/116/";
  topic = topic.httpGet();
  topic.dump();
}

